package cloud.xlh.infrastructure.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * Created with IntelliJ IDEA.
 *
 * @ project name : xlh_osms
 * @ Author: XuLeHuang
 * @ Date: 2023/1/17 16:34
 * @ Description: 金额处理 参考：https://juejin.cn/post/7121852516228136996?share_token=21aef5e1-01f6-4c12-840a-1e9f6b63aa97
 */
public class AmtHandleUtil {

    /**基本数据类型：转BigDecimal要么new BigDecimal(将其他类型转为String)，要么就下面的方法
    * */

    /**
     * 整型
     * */
    public BigDecimal intToBigDecimal(int amt) {
        return BigDecimal.valueOf(amt);
    }

    /**
     * 单精度浮点型
     * */
    public BigDecimal floatToBigDecimal(float amt) {
        return BigDecimal.valueOf(amt);
    }

    /**
     * 双精度浮点型
     * */
    public BigDecimal doubleToBigDecimal(Double amt) {
        return BigDecimal.valueOf(amt);
    }

    /**
     * 字符串
     * */
    public BigDecimal stringToBigDecimal(String amt) {
        return new BigDecimal(amt);
    }

   /**
    *
    * @param var1
    * @param var2
    * @return int
    * @author XuLeHuang
    * @create 2023/1/17
    * @name: 金额比较-值比较
    * @Description: var1 < var2 return -1 ; var1 = var2 return 0 ; var1 > var2 return 1
    *               equal()除了值比较还有精度比较
    **/
   public int compareTo(BigDecimal var1 , BigDecimal var2) {
        return var1.compareTo(var2);
    }

    /**
     *
     * @param var1
     * @param var2
     * @return java.lang.Boolean
     * @author XuLeHuang
     * @create 2023/1/17
     * @name: 金额比较-值比较+精度比较
     * @Description:
     **/
    public Boolean equals(BigDecimal var1 , BigDecimal var2) {
       return var1.equals(var2);
    }

    /**
     *
     * @param var1
     * @param formatCode
     * @return java.lang.String
     * @author XuLeHuang
     * @create 2023/1/17
     * @name: 字符串转BigDecimal
     * @Description: 三种格式
     **/
    public String stringToBigDecimal(BigDecimal var1 , Integer formatCode) {
        if (Format.NONE_FE.getCode() == formatCode) {
            return var1.toPlainString();
        } else if (Format.USER_FE_FOR_REQUIRE.getCode() == formatCode) {
            return var1.toString();
        } else if (Format.ENGINEERING.getCode() == formatCode) {
            return var1.toEngineeringString();
        } else {
            return var1.toPlainString();
        }
    }

    /**
     *
     * @param var1
     * @param var2
     * @param operatorSymbol 运算符
     * @param precision 精度  RoundingMode枚举类
     * @return java.math.BigDecimal
     * @author XuLeHuang
     * @create 2023/1/17
     * @name: 算数运算
     * @Description:
     * 基本结论：在使用BigDecimal进行（所有）运算时，一定要明确指定精度和舍入模式。
     * 拓展一下，舍入模式定义在RoundingMode枚举类中，共有8种：
     *
     * RoundingMode.UP：舍入远离零的舍入模式。在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。注意，此舍入模式始终不会减少计算值的大小。
     * RoundingMode.DOWN：接近零的舍入模式。在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1，即截短)。注意，此舍入模式始终不会增加计算值的大小。
     * RoundingMode.CEILING：接近正无穷大的舍入模式。如果 BigDecimal 为正，则舍入行为与 ROUNDUP 相同;如果为负，则舍入行为与 ROUNDDOWN 相同。注意，此舍入模式始终不会减少计算值。
     * RoundingMode.FLOOR：接近负无穷大的舍入模式。如果 BigDecimal 为正，则舍入行为与 ROUNDDOWN 相同;如果为负，则舍入行为与 ROUNDUP 相同。注意，此舍入模式始终不会增加计算值。
     * RoundingMode.HALF_UP：向“最接近的”数字舍入，如果与两个相邻数字的距离相等，则为向上舍入的舍入模式。如果舍弃部分 >= 0.5，则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。注意，这是我们在小学时学过的舍入模式(四舍五入)。
     * RoundingMode.HALF_DOWN：向“最接近的”数字舍入，如果与两个相邻数字的距离相等，则为上舍入的舍入模式。如果舍弃部分 > 0.5，则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。
     * RoundingMode.HALF_EVEN：向“最接近的”数字舍入，如果与两个相邻数字的距离相等，则向相邻的偶数舍入。如果舍弃部分左边的数字为奇数，则舍入行为与 ROUNDHALFUP 相同;如果为偶数，则舍入行为与 ROUNDHALF_DOWN 相同。注意，在重复进行一系列计算时，此舍入模式可以将累加错误减到最小。此舍入模式也称为“银行家舍入法”，主要在美国使用。四舍六入，五分两种情况。如果前一位为奇数，则入位，否则舍去。以下例子为保留小数点1位，那么这种舍入方式下的结果。1.15 ==> 1.2 ,1.25 ==> 1.2
     * RoundingMode.UNNECESSARY：断言请求的操作具有精确的结果，因此不需要舍入。如果对获得精确结果的操作指定此舍入模式，则抛出ArithmeticException。
     *
     * 通常我们使用的四舍五入即RoundingMode.HALF_UP。
     *
     * 作者：程序新视界
     * 链接：https://juejin.cn/post/7121852516228136996
     * 来源：稀土掘金
     * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
     **/
    public BigDecimal compute(BigDecimal var1 , BigDecimal var2 , Integer operatorSymbol , RoundingMode precision) {
        if (OperatorSymbol.ADD.getCode() == operatorSymbol) {
            return var1.add(var2);
        } else if (OperatorSymbol.SUBTRACT.getCode() == operatorSymbol) {
            return var1.subtract(var2);
        } else if (OperatorSymbol.MULTIPLY.getCode() == operatorSymbol) {
            return var1.multiply(var2);
        } else if (OperatorSymbol.DIVIDE.getCode() == operatorSymbol) {
            return  var1.divide(var2 , precision);
        } else {
            return null;
        }
    }

    /**
     *运算符 枚举
     * */
    public static enum OperatorSymbol {
        ADD(00, "加法"),
        SUBTRACT(01, "减法"),
        MULTIPLY(10, "乘法"),
        DIVIDE(11, "除法");

        private static final Set<Integer> enumSet = new HashSet<>(2);

        static {
            Arrays.asList(AmtHandleUtil.OperatorSymbol.values()).forEach(e -> enumSet.add(e.getCode()));
        }

        public static boolean hasEnum(Integer code) {
            return enumSet.contains(code);
        }


        private Integer code;

        private String desc;

        OperatorSymbol(Integer code, String desc) {
            this.code = code;
            this.desc = desc;
        }

        public Integer getCode() {
            return code;
        }

        public String getDesc() {
            return desc;
        }

        public static String getDesc(Integer code) {
            AmtHandleUtil.OperatorSymbol[] values = values();
            if (code == null)
                return null;
            for (AmtHandleUtil.OperatorSymbol value : values) {
                if (value.code.equals(code)) {
                    return value.desc;
                }
            }
            return null;
        }
    }

    /**
     *格式 枚举
     * */
    public static enum Format {
        NONE_FE(00, "不使用科学计数法"),
        USER_FE_FOR_REQUIRE(01, "必要的时候使用科学计数法"),
        ENGINEERING(10, "必要的时候使用工程计数法");

        private static final Set<Integer> enumSet = new HashSet<>(2);

        static {
            Arrays.asList(AmtHandleUtil.Format.values()).forEach(e -> enumSet.add(e.getCode()));
        }

        public static boolean hasEnum(Integer code) {
            return enumSet.contains(code);
        }


        private Integer code;

        private String desc;

        Format(Integer code, String desc) {
            this.code = code;
            this.desc = desc;
        }

        public Integer getCode() {
            return code;
        }

        public String getDesc() {
            return desc;
        }

        public static String getDesc(Integer code) {
            AmtHandleUtil.Format[] values = values();
            if (code == null)
                return null;
            for (AmtHandleUtil.Format value : values) {
                if (value.code.equals(code)) {
                    return value.desc;
                }
            }
            return null;
        }
    }
}
